// Copyright 2011 Jay Young. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview
 *
 * Provides an auth manager for the Relieved blog.
 */


goog.provide('rb.auth.AuthManager');

goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventType');
goog.require('goog.soy');

goog.require('rb.auth.AuthCheckCommand');
goog.require('rb.auth.UpdateUserCommand');
goog.require('rb.auth.templates');

goog.require('relief.auth.AuthChangeEvent');
goog.require('relief.auth.AuthManager');
goog.require('relief.auth.EventType');



/**
 * An AuthManager implementation that can talk to the Relieved back-end.
 *
 * @param {relief.rpc.RPCService} rpc The app's RPC service.
 *
 * @constructor
 * @extends {relief.auth.AuthManager}
 */
rb.auth.AuthManager = function(rpc) {
  goog.base(this);

  /**
   * @type {relief.rpc.RPCService}
   * @private
   */
  this.rpc_ = rpc;

  /**
   * Whether a request to the server is currently in progress.
   * @type {boolean}
   * @private
   */
  this.pendingRequest_ = false;

  /**
   * Array of callbacks to be executed when a request returns.
   *
   * @type {!Array.<function(relief.auth.User)>}
   * @private
   */
  this.listeners_ = [];

  /**
   * The current user.
   * @type {rb.auth.User}
   * @private
   */
  this.user_ = null;
};
goog.inherits(rb.auth.AuthManager, relief.auth.AuthManager);


/**
 * Takes a callback and calls it with an appropriate rb.auth.User object.
 *
 * @param {function(rb.auth.User)} callback The client's callback.
 * @override
 */
rb.auth.AuthManager.prototype.getCurrentUser = function(callback) {
  if (this.pendingRequest_) {
    this.listeners_.push(callback);
  }
  else {
    var user = this.user_;
    if (user) {
      callback(user.clone());
    }
    else {
      this.pendingRequest_ = true;
      this.listeners_.push(callback);

      var authCheck =
          new rb.auth.AuthCheckCommand(goog.bind(this.gotUser_, this));
      this.rpc_.execute(authCheck);
    }
  }
};


/**
 * Callback called when the server check returns.
 *
 * @param {rb.auth.User} user The new user object.
 * @private
 */
rb.auth.AuthManager.prototype.gotUser_ = function(user) {
  this.user_ = user;

  if (user.isNewUser()) {
    this.registerUser_(user);
  }
  else {
    this.dispatchUser_(user);
  }
};


/**
 * The user that just logged in has not registered here before.  Get their name.
 *
 * @param {rb.auth.User} user The new user.
 * @private
 */
rb.auth.AuthManager.prototype.registerUser_ = function(user) {
  var el = goog.soy.renderAsElement(rb.auth.templates.register,
                                    {defaultName: user.getName()});
  document.body.appendChild(el);

  /**
   * See onEnterPress_.
   *
   * @type {boolean}
   * @private
   */
  this.skipAnonymous_ = false;

  /**
   * @type {Element}
   * @private
   */
  this.registerEl_ = el;

  var form = goog.dom.getElement('rb-auth-register'),
      displayName = form['display-name'],
      checkUnique = form['check-unique'],
      beAnon = form['be-anonymous'],
      handler = new goog.events.EventHandler(this);

  displayName.focus();

  handler.listen(beAnon, goog.events.EventType.CLICK, this.setAnonymous_);
  handler.listen(form, goog.events.EventType.SUBMIT, this.onSubmit_);
  handler.listen(displayName, goog.events.EventType.KEYDOWN,
                 this.onEnterPress_);

  /**
   * @type {goog.events.EventHandler}
   * @private
   */
  this.handler_ = handler;
};


/**
 * Event handler for pressing the Enter key while in the "display name" input
 * box.  Because of the button order, "Be Anonymous" is the submit button
 * that gets "clicked" when the user presses "Enter" in the input box.  Catch
 * the Enter keypress, and set a flag so the "Be Anonymous" click handler
 * ignores the click.
 *
 * @param {goog.events.BrowserEvent} e The keypress event.
 * @private
 */
rb.auth.AuthManager.prototype.onEnterPress_ = function(e) {
  this.skipAnonymous_ = true;
};


/**
 * Set the user's display name to be "Anonymous".  The "Be Anonymous" button is
 * also a Submit button, but this event will fire first.  Set the name and let
 * the onSubmit method handlet he rest.
 *
 * @param {goog.events.BrowserEvent} e The click event.
 * @private
 */
rb.auth.AuthManager.prototype.setAnonymous_ = function(e) {
  if (! this.skipAnonymous_) {
    var input = /** @type {HTMLInputElement} */
        (goog.dom.getElement('rb-auth-register')['display-name']);
    input.value = 'Anonymous';
  }
};


/**
 * Handles the registration form's onSubmit event.
 * @param {goog.events.BrowserEvent} e The onSubmit event.
 *
 * @private
 */
rb.auth.AuthManager.prototype.onSubmit_ = function(e) {
  var displayName = e.target['display-name'].value;

  this.handler_.dispose();
  this.handler_ = null;

  var el = this.registerEl_;
  this.registerEl_ = null;
  document.body.removeChild(el);

  this.updateUser_(displayName);
  e.preventDefault();
};


/**
 * Send a request to the server to store the user's display name.
 * @param {string} name The user's new display name.
 * @private
 */
rb.auth.AuthManager.prototype.updateUser_ = function(name) {
  var user = this.user_;
  user.setName(name);

  var updateCmd =
      new rb.auth.UpdateUserCommand(goog.nullFunction, goog.nullFunction,
                                    name, user.getId()),
      rpc = this.rpc_;

  rpc.execute(updateCmd);
  this.dispatchUser_(user);
};


/**
 * Dispatches the new user to any listeners.
 * @param {rb.auth.User} user The user to object to send out.
 *
 * @private
 */
rb.auth.AuthManager.prototype.dispatchUser_ = function(user) {
  this.pendingRequest_ = false;
  user = user.clone();

  var listeners = this.listeners_;
  for (var i = 0, len = listeners.length; i < len; ++i) {
    listeners[i](user);
  }

  this.listeners_ = [];

  var event = /** @type {relief.auth.AuthChangeEvent} */ ({
    type: relief.auth.EventType.AUTH_CHANGE,
    user: user
  });

  this.dispatchEvent(event);
};


/**
 * @inheritDoc
 */
rb.auth.AuthManager.prototype.disposeInternal = function() {
  var handler = this.handler_;
  if (handler) {
    handler.dispose();
    this.handler_ = null;
  }

  this.pendingRequest_ = false;
  this.rpc_ = this.user_ = null;

  this.listeners_ = [];

  goog.base(this, 'disposeInternal');
};
